
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <iomanip>

using namespace std;

/***
 * @brief 获取当前时间字符串
 * @param fmt 时间格式，默认"%Y-%m-%d %H:%M:%S"
 * @param time_zone 时区，默认东八区
 * @return 返回格式化后的时间字符串
 */
static std::string GetNow(
    const char *fmt = "%Y-%m-%d %H:%M:%S",
    int time_zone = 8)
{
    std::time_t unix_sec = std::time(nullptr);
    std::tm tm;
    static const int kHoursInDay = 24;
    static const int kMinutesInHour = 60;
    static const int kDaysFromUnixTime = 2472632;
    static const int kDaysFromYear = 153;
    static const int kMagicUnkonwnFirst = 146097;
    static const int kMagicUnkonwnSec = 1461;
    tm.tm_sec = unix_sec % kMinutesInHour;
    int i = (unix_sec / kMinutesInHour);
    tm.tm_min = i % kMinutesInHour; // nn
    i /= kMinutesInHour;
    tm.tm_hour = (i + time_zone) % kHoursInDay; // hh
    tm.tm_mday = (i + time_zone) / kHoursInDay;
    int a = tm.tm_mday + kDaysFromUnixTime;
    int b = (a * 4 + 3) / kMagicUnkonwnFirst;
    int c = (-b * kMagicUnkonwnFirst) / 4 + a;
    int d = ((c * 4 + 3) / kMagicUnkonwnSec);
    int e = -d * kMagicUnkonwnSec;
    e = e / 4 + c;
    int m = (5 * e + 2) / kDaysFromYear;
    tm.tm_mday = -(kDaysFromYear * m + 2) / 5 + e + 1;
    tm.tm_mon = (-m / 10) * 12 + m + 2;
    tm.tm_year = b * 100 + d - 6700 + (m / 10);
    stringstream ss;
    ss << std::put_time(&tm, fmt); // #include <iomanip>
    return ss.str();
}

/********************************************** 以上为题目提供代码 ***********************************************************************/

enum class LogLevel
{
    DEBUG,
    INFO,
    ERROR,
    FATAL
};
string _logformat{""};
LogLevel _loglevel{LogLevel::DEBUG};

string to_string(const LogLevel level)
{
    switch (level)
    {
    case LogLevel::DEBUG:
        return "debug";
    case LogLevel::INFO:
        return "info";
    case LogLevel::ERROR:
        return "error";
    case LogLevel::FATAL:
        return "fatal";
    default:
        return "unknown";
    }
}

LogLevel from_string(const string level)
{
    if (level == "debug")
        return LogLevel::DEBUG;
    else if (level == "info")
        return LogLevel::INFO;
    else if (level == "error")
        return LogLevel::ERROR;
    else if (level == "fatal")
        return LogLevel::FATAL;
    else
        return LogLevel::DEBUG; // 默认返回DEBUG级别
}

/***
 * @brief
 * @param level 日志级别
 * @param format 日志格式
 * @param log 日志内容
 * @param file 文件
 * @param line 行号
 */
void LogWriteFormat(const LogLevel level, string format, const string log, const string file, const int line)
{
    // format: "{time} {level} {context} {time} {file}:{line}"
    size_t pos = 0;

    while ((pos = format.find("{time}")) != string::npos)
    {
        format.replace(pos, 6, GetNow());
    }

    while ((pos = format.find("{level}")) != string::npos)
    {
        format.replace(pos, 7, to_string(level));
    }

    while ((pos = format.find("{context}")) != string::npos)
    {
        format.replace(pos, 9, log);
    }

    while ((pos = format.find("{file}")) != string::npos)
    {
        format.replace(pos, 6, file);
    }

    while ((pos = format.find("{line}")) != string::npos)
    {
        format.replace(pos, 6, to_string(line));
    }

    cout << format << std::endl;
}

/***
 * @brief
 * @param level 日志级别
 * @param log 日志内容
 * @param file 文件
 * @param line 行号
 */
void LogWrite(const LogLevel level, const string log, const string file, const int line)
{
    if (level < _loglevel)
    {
        // 低于当前日志级别，不输出
        return;
    }

    if (!_logformat.empty())
    {
        // 自定义格式
        LogWriteFormat(level, _logformat, log, file, line);
    }
    else
    {
        // 默认格式
        cout
            << GetNow() << " "
            << to_string(level) << " "
            << log << " "
            << file << ":" << line
            << std::endl;
    }
}

/*********************************************** 以上为解题代码 ****************************************************************/ 

// 设置日志级别
void LogSetLevel(const LogLevel level) { _loglevel = level; }

// 设置日志格式
void LogSetFormat(const string format) { _logformat = format; }

#define LOGDEBUG(log) LogWrite(LogLevel::DEBUG, log, __FILE__, __LINE__)
#define LOGINFO(log) LogWrite(LogLevel::INFO, log, __FILE__, __LINE__)
#define LOGERROR(log) LogWrite(LogLevel::ERROR, log, __FILE__, __LINE__)
#define LOGFATAL(log) LogWrite(LogLevel::FATAL, log, __FILE__, __LINE__)

/*********************************************** 以上Log 日志系统提供用户调用的函数与宏（对外接口） ****************************************************************/ 

/*********************************************** 一下为测试代码 ****************************************************************/ 

// applog debug "{time} {level} {message} {file}:{line}"
int main(int argc, char const *argv[])
{

    switch (argc)
    {
    case 3:
        // 如果有三个参数，第3个参数是日志格式
        LogSetFormat(argv[2]);
    case 2:
        // 如果有两个参数，第2个参数是日志级别

        // "info" ==> LogLevel::INFO
        _loglevel = from_string(argv[1]);
        break;
    }

    LOGDEBUG("调试信息");
    LOGINFO("程序运行中");
    LOGERROR("出错了！！！！");
    LOGDEBUG("调试信息");

    LOGFATAL("严重错误，程序无法继续运行");


    LOGDEBUG("调试信息");

    return 0;
}
